import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import Stats from 'three/examples/jsm/libs/stats.module'

export class Scene {
  /**
   * three 3D场景及显示的构造器
   * @constructor
   * @param {object} container -用于存放3d渲染效果的画布容器
   * @param {number} size -渲染场景最长边的长度，单位 米
   * @param {number} fps -页面渲染帧率
   */
  constructor(container, size=200, fps=60) {
    // 获取容器及其长宽
    this.container = container;
    this.height = container.clientHeight;
    this.width = container.clientWidth;
    // 获取地图的最大边长
    this.size = size;
    // 初始化渲染器并设置背景颜色
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setSize(this.width, this.height);
    this.renderer.setClearColor(0x000623, 0.8);
    this.container.appendChild(this.renderer.domElement);
    // 初始化场景模块
    this.scene = new THREE.Scene();
    // 初始化相机
    this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 2000);
    this.camera.position.x = this.size / 10;
    this.camera.position.y = this.size / 10;
    this.camera.position.z = this.size / 10;
    this.camera.up.x = 0;
    this.camera.up.y = 1;
    this.camera.up.z = 0;
    this.camera.lookAt({ x: 0, y: 0, z: 0 });
    // 初始化灯光
    var ambient = new THREE.AmbientLight(0xeeeeee);
    this.scene.add(ambient);
    // 启动动画
    this._startAnimate(fps);
    // 启用界面手操功能，限制相机距离在全景至1/40的范围
    this.handle = new OrbitControls(this.camera, this.renderer.domElement);
    this.handle.minDistance = this.size / 40 > 1 ? this.size / 40 : 1;
    this.handle.maxDistance = this.size < 2000 ? this.size : 2000;
    this.handle.maxPolarAngle = Math.PI / 2.05;
  }
  /**
   * 在场景中添加三维的笛卡尔坐标系
   * @param {number} length -可视坐标轴的长度
   */
  displayAxes(length) {
    let _length = this.size / 2;
    if(length && length > 2 && length < 200) {
      _length = length;
    }
    var axes = new THREE.AxesHelper(_length);
    this.scene.add(axes);
  }
  /**
   * 在场景的地表平面添加网格地板
   * @param {number} unit -每个网格的边长，单位 米
   * @param {number} size -可视网格的全边长，单位 米
   */
  displayGrid(unit=1, size=this.size) {
    let divisions = size / unit;
    var grid = new THREE.GridHelper(size, divisions, 0x444444, 0x444444);
    this.scene.add(grid);
  }
  /**
   * 在渲染界面四角之一显示当前的FPS值等状态信息
   * @param {boolean} left -为true贴近左侧，为false则贴近右侧
   * @param {boolean} top -为true贴近上侧，为false则贴近下侧
   */
  displayStats(left=true, top=true) {
    this.stats = new Stats();
    this.stats.domElement.style.position = 'absolute';

    if(typeof(left) !== "boolean" || typeof(top) !== "boolean") {
      this.stats.domElement.style.left = 0;
      this.stats.domElement.style.top = 0;
      this.container.appendChild(this.stats.domElement);
      return;
    }

    if(left === true) {
      this.stats.domElement.style.left = 0;
    } else {
      this.stats.domElement.style.left = null;
      this.stats.domElement.style.right = 0;
    }
    if(top === true) {
      this.stats.domElement.style.top = 0;
    } else {
      this.stats.domElement.style.top = null;
      this.stats.domElement.style.bottom = 0;
    }
    this.container.appendChild(this.stats.domElement);
  }
  /**
   * 重置渲染视窗，将相机位置恢复到初始状态
   */
  initialView() {
    console.log('还没实现呢');
    // TODO
  }
  /**
   * 动画渲染启动函数，仅供内部调用
   * @param {private number} fps -页面渲染帧率
   */
  _startAnimate(fps) {
    setTimeout( () => {
      this.stats && this.stats.begin();
      this.renderer.render(this.scene, this.camera);
      requestAnimationFrame(this._startAnimate.bind(this));
      this.stats && this.stats.end();
    }, 1000 / fps );
  }
  appendModel(model) {
    model.todo();
  }
  deleteModel(model) {
    model.todo();
  }
}

export class StaticModel {
  LOADFINISH = false;
  constructor(modelFile, modelName, scene) {
    var index = modelFile.lastIndexOf('.');
    var suffix = modelFile.substr(index+1);
    var loader = null;
    if(suffix === 'gltf' || suffix === 'glb') {
      loader = new GLTFLoader();
      loader.load(modelFile, m => {
        this.model = m.scene;
        this.model.name = modelName;
        this.serModelPosition();
        scene.scene.add(this.model);
      }, (xhr) => {
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
      })
    } else {
      throw "The file type must be 'gltf' or 'glb'!";
    }
  }
  // TODO 模型异步加载问题暂未解决
  serModelPosition(x=0, y=0, z=0) {
    this.model.position.set(y, z, x);
  }
}

